package com.zsd.android.webrtcdemo.connection;

import android.content.Context;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Looper;

import com.zsd.android.webrtcdemo.interfaces.IViewCallback;
import com.zsd.android.webrtcdemo.socket.JavaWebSocket;

import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
import org.webrtc.Camera1Enumerator;
import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraEnumerator;
import org.webrtc.CameraVideoCapturer;
import org.webrtc.DataChannel;
import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.DefaultVideoEncoderFactory;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.RtpReceiver;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoDecoderFactory;
import org.webrtc.VideoEncoderFactory;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;
import org.webrtc.audio.JavaAudioDeviceModule;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class PeerConnectionManager {

    private static volatile PeerConnectionManager instance;
    private List<PeerConnection> peerConnections;
    private boolean videoEnable;
    private ExecutorService executor;
    private PeerConnectionFactory factory;
    private Context mContext;
    private EglBase rootEglBase;
    private String mId;
    private MediaStream localStream;
    //音频源
    private AudioSource audioSource;
    //音频轨
    private AudioTrack audioTrack;
    //获取摄像头的设备
    private VideoCapturer videoCapturer;
    //视频源
    private VideoSource videoSource;
    //视频轨
    private VideoTrack videoTrack;
    //帮助渲染的本地预览
    private SurfaceTextureHelper surfaceTextureHelper;
    //ICE服务器的集合
    private ArrayList<PeerConnection.IceServer> iceServers;
    //会议室所有用户的Id
    private ArrayList<String> connectionIds;
    //会议室每一个用户会对本地实现一个p2p连接Peer(PeerConnection)
    private HashMap<String, Peer> connectionPeerDic;
    private JavaWebSocket webSocket;
    private Handler mHandler;
    private IViewCallback viewCallback;
    //声音服务类
    private AudioManager audioManager;
    //当前客户端的角色
    private Role role;

    //角色
    enum Role {
        Caller,//邀请者
        Receiver//被邀请者
    }

    private PeerConnectionManager() {
        executor = Executors.newSingleThreadExecutor();
        mHandler = new Handler(Looper.getMainLooper());
    }

    public static PeerConnectionManager getInstance() {
        PeerConnectionManager manager = instance;
        if (manager == null) {
            synchronized (PeerConnectionManager.class) {
                if (manager == null) {
                    manager = new PeerConnectionManager();
                    instance = manager;
                }
            }
        }
        return manager;
    }

    public void initContext(Context mContext, EglBase rootEglBase) {
        this.mContext = mContext;
        this.rootEglBase = rootEglBase;
        iceServers = new ArrayList<>();
        //stun:stun2.l.google.com:19302
        PeerConnection.IceServer stunService = PeerConnection.IceServer.builder("stun:47.107.132.117:3478?transport=udp")
                .setUsername("")
                .setPassword("")
                .createIceServer();

        PeerConnection.IceServer turnService = PeerConnection.IceServer.builder("turn:47.107.132.117:3478?transport=udp")
                .setUsername("ddsingsong")
                .setPassword("123456")
                .createIceServer();
        iceServers.add(stunService);
        iceServers.add(turnService);
        connectionIds = new ArrayList<>();
        connectionPeerDic = new HashMap<>();
        audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
    }

    /**
     * @param socketId     对方的id
     * @param iceCandidate
     */
    public void onRemoteIceCandidate(String socketId, IceCandidate iceCandidate) {
        //通过SocketId取出连接对象
        Peer peer = connectionPeerDic.get(socketId);
        if (peer != null) {
            peer.peerConnection.addIceCandidate(iceCandidate);
        }
    }

    public void onReceiverAnswer(String socketId, String sdp) {
        executor.execute(() -> {
            Peer peer = connectionPeerDic.get(socketId);
            //对方的对话sdp
            SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.ANSWER, sdp);
            if (peer != null) {
                peer.peerConnection.setRemoteDescription(peer, sessionDescription);
            }
        });
    }

    public void joinRoom(JavaWebSocket javaWebSocket, ArrayList<String> connections, String mId, boolean videoEnable) {
        this.webSocket = javaWebSocket;
        this.mId = mId;
        this.videoEnable = videoEnable;
        executor.execute(() -> {
            if (factory == null) {
                factory = createConnectionFactory();
            }
            if (localStream == null) {
                //创建本地流
                createLocalMediaStream();
            }
            connectionIds.addAll(connections);
            //创建本地连接
            createPeerConnections();
            //本地的数据流推向会议室的每一个人的能力
            addStreams();
            //发送邀请
            createOffers();
        });
    }

    /**
     * 设置是否静音
     *
     * @param enableMic
     */
    public void toggleSpeaker(boolean enableMic) {
        if (audioTrack != null) {
            //切换是否允许将本地的麦克风数据推送的远端
            audioTrack.setEnabled(enableMic);
        }
    }

    /**
     * 设置是否开启免提
     *
     * @param enableSpeaker
     */
    public void toggleLager(boolean enableSpeaker) {
        if (audioManager != null) {
            audioManager.setSpeakerphoneOn(enableSpeaker);
        }
    }

    /**
     * 是否打开摄像头
     *
     * @param enableCamera
     */
    public void toggleOpenCamera(boolean enableCamera) {
        if (videoTrack != null) {
            videoTrack.setEnabled(enableCamera);
        }
    }

    /**
     * 切换摄像头
     */
    public void toggleCamera() {
        if (videoCapturer == null) {
            return;
        }
        if (videoCapturer instanceof CameraVideoCapturer) {
            CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer) videoCapturer;
            cameraVideoCapturer.switchCamera(null);
        }
    }

    /**
     * 挂断
     */
    public void handUp() {

    }

    /**
     * 退出房间
     * 耗时操作
     */
    public void exitRoom() {
        executor.execute(() -> {
            ArrayList<String> mCopy = (ArrayList<String>) connectionIds.clone();
            for (String id : mCopy) {
                closePeerConnection(id);
            }
            //清空集合
            if (connectionIds != null) {
                connectionIds.clear();
            }
            //关闭音频推流
            if (audioSource != null) {
                audioSource.dispose();
                audioSource = null;
            }
            //关闭视频推流
            if (videoSource != null) {
                videoSource.dispose();
                videoSource = null;
            }
            //关闭摄像头预览
            if (videoCapturer != null) {
                try {
                    videoCapturer.stopCapture();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            if (surfaceTextureHelper != null) {
                surfaceTextureHelper.dispose();
                surfaceTextureHelper = null;
            }
            //关闭工厂
            if (factory != null) {
                factory.dispose();
                factory = null;
            }
        });
    }

    private void closePeerConnection(String id) {
        Peer peer = connectionPeerDic.get(id);
        if (peer != null) {
            //关闭p2p连接
            peer.peerConnection.close();
            connectionPeerDic.remove(id);
            connectionIds.remove(id);
            if (viewCallback != null) {
                mHandler.post(() -> {
                    viewCallback.onCloseWithId(id);
                });
            }
        }
    }

    /**
     * 创建PeerConnectionFactory
     *
     * @return
     */
    private PeerConnectionFactory createConnectionFactory() {
        //初始化
        PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(mContext).createInitializationOptions());

        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
        //视频编码
        VideoEncoderFactory videoEncoderFactory = new DefaultVideoEncoderFactory(rootEglBase.getEglBaseContext(), true, true);
        //视频解码
        VideoDecoderFactory videoDecoderFactory = new DefaultVideoDecoderFactory(rootEglBase.getEglBaseContext());
        return PeerConnectionFactory.builder()
                //入参出参
                .setOptions(options)
                .setAudioDeviceModule(JavaAudioDeviceModule.builder(mContext).createAudioDeviceModule())
                .setVideoEncoderFactory(videoEncoderFactory)
                .setVideoDecoderFactory(videoDecoderFactory)
                .createPeerConnectionFactory();
    }

    /**
     * 创建MediaStream
     *
     * @return
     */
    private void createLocalMediaStream() {
        localStream = factory.createLocalMediaStream("ARDAMS");
        //创建音频源
        audioSource = factory.createAudioSource(createAudioSource());
        //采集音频
        audioTrack = factory.createAudioTrack("ARDAMSa0", audioSource);
        localStream.addTrack(audioTrack);
        if (videoEnable) {
            //创建视频源
            videoCapturer = createVideoCapture();
            videoSource = factory.createVideoSource(videoCapturer.isScreencast());
            surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", rootEglBase.getEglBaseContext());
            //初始化
            videoCapturer.initialize(surfaceTextureHelper, mContext, videoSource.getCapturerObserver());
            //摄像头预览的 width, height, 帧率
            videoCapturer.startCapture(320, 240, 10);
            //视频轨
            videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
            localStream.addTrack(videoTrack);
            if (viewCallback != null) {
                mHandler.post(() -> {
                    viewCallback.onSetLocalStream(localStream, mId);
                });
            }
        }
    }

    /**
     * 创建MediaConstraints
     *
     * @return
     */
    private MediaConstraints createAudioSource() {
        MediaConstraints audioConstraints = new MediaConstraints();
        //回声消除
        audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googEchoCancellation", "true"));
        //自动增益
        audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAutoGainControl", "false"));
        //高音过滤
        audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googHighpassFilter", "false"));
        //噪音抑制
        audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googNoiseSuppression", "true"));
        return audioConstraints;
    }

    private VideoCapturer createVideoCapture() {
        CameraEnumerator enumerator = null;
        if (Camera2Enumerator.isSupported(mContext)) {
            enumerator = new Camera2Enumerator(mContext);
        } else {
            enumerator = new Camera1Enumerator(true);
        }
        String[] deviceNames = enumerator.getDeviceNames();
        for (String deviceName : deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }

        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        return null;
    }

    /**
     * 建立对会议室每一个用户的链接
     */
    private void createPeerConnections() {
        for (String connectionId : connectionIds) {
            Peer peer = new Peer(connectionId);
            connectionPeerDic.put(connectionId, peer);
        }
    }

    /**
     * 为所有连接添加流
     */
    private void addStreams() {
        for (Map.Entry<String, Peer> entry : connectionPeerDic.entrySet()) {
            if (localStream == null) {
                createLocalMediaStream();
            }
            Peer peer = entry.getValue();
            peer.peerConnection.addStream(localStream);
        }
    }

    /**
     * 为所有连接创建Offer
     */
    private void createOffers() {
        for (Map.Entry<String, Peer> entry : connectionPeerDic.entrySet()) {
            role = Role.Caller;
            Peer peer = entry.getValue();
            //给每一个会议室的人发送邀请，并且传递我的数据类型（音频，视频选择）
            //发送成功会调用SdpObserver接口的onCreateSuccess，发送失败会调用SdpObserver接口的onCreateFailure
            peer.peerConnection.createOffer(peer, offerOrAnswerConstraint());
        }
    }

    /**
     * 设置传输音视频
     * 音频必须传输
     * 视频()不一定
     *
     * @return 媒体约束
     */
    private MediaConstraints offerOrAnswerConstraint() {
        MediaConstraints mediaConstraints = new MediaConstraints();
        ArrayList<MediaConstraints.KeyValuePair> keyValuePairs = new ArrayList<>();
        //音频必须传输
        keyValuePairs.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        //视频不一定
        keyValuePairs.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", String.valueOf(videoEnable)));
        mediaConstraints.mandatory.addAll(keyValuePairs);
        return mediaConstraints;
    }

    public void setViewCallback(IViewCallback viewCallback) {
        this.viewCallback = viewCallback;
    }

    private class Peer implements PeerConnection.Observer, SdpObserver {

        //本机端和远端用户的链接
        private PeerConnection peerConnection;
        //其他用户的id
        private String socketId;

        public Peer(String socketId) {
            this.socketId = socketId;

            PeerConnection.RTCConfiguration rtcConfiguration = new PeerConnection.RTCConfiguration(iceServers);
            peerConnection = factory.createPeerConnection(rtcConfiguration, this);
        }

        /**
         * 网络状态发生改变 4G ---> wifi
         *
         * @param signalingState
         */
        @Override
        public void onSignalingChange(PeerConnection.SignalingState signalingState) {

        }

        /**
         * 连接上了ICE服务器
         *
         * @param iceConnectionState
         */
        @Override
        public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {

        }

        @Override
        public void onIceConnectionReceivingChange(boolean b) {

        }

        @Override
        public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {

        }

        /**
         * 调用时机有两类
         * 第一类在连接到ICE服务器的时候，调用次数是网络中有多少个路由节点（1-n）
         * 第二类在有人进入房间的时候，对方的ICE服务器的路由节点，调用次数是视频通话的人在网络中离ICE服务器有多少个路由节点（1-n）
         *
         * @param iceCandidate
         */
        @Override
        public void onIceCandidate(IceCandidate iceCandidate) {
            //socket去分发
            webSocket.sendIceCandidate(socketId, iceCandidate);
        }

        @Override
        public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {

        }

        /**
         * p2p建立成功之后
         * 在子线程调用
         *
         * @param mediaStream 视频流和音频流
         */
        @Override
        public void onAddStream(MediaStream mediaStream) {
            if (viewCallback != null) {
                mHandler.post(() -> {
                    viewCallback.onAddRemoteStream(mediaStream, socketId);
                });
            }
        }

        @Override
        public void onRemoveStream(MediaStream mediaStream) {

        }

        @Override
        public void onDataChannel(DataChannel dataChannel) {

        }

        @Override
        public void onRenegotiationNeeded() {

        }

        @Override
        public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {

        }

        //------------------------以上Observer------------------------------
        //------------------------以下SdpObserver------------------------------
        @Override
        public void onCreateSuccess(SessionDescription sessionDescription) {
            //设置本地的SDP，如果成功回调onSetSuccess，如果失败回调onSetFailure
            peerConnection.setLocalDescription(this, sessionDescription);
        }

        @Override
        public void onSetSuccess() {
            //交换彼此的SDP， IceCandidates
            if (peerConnection.signalingState() == PeerConnection.SignalingState.HAVE_LOCAL_OFFER) {
                //webSocket主动交换
                webSocket.sendOffer(socketId, peerConnection.getLocalDescription());
            }

        }

        @Override
        public void onCreateFailure(String s) {

        }

        @Override
        public void onSetFailure(String s) {

        }
    }
}
